#include "MotorJuego.h"
#include "MotorAtaque.h"
#include "MotorDesplazamiento.h"
#include "MotorLanzadorPajaritos.h"
#include "MotorColisiones.h"
#include "Cerdito.h"

using namespace TP::Juego::Motorizacion;
using namespace TP::Juego::Arena::Utilidades;

MotorJuego::MotorJuego() { }

MotorJuego::~MotorJuego() {
	// Libero la coleccion de partidas.
	// this->_partidas.clear();

	// Elimino el motor de desplazamiento.
	if (this->_motorDesplazamiento)
		delete this->_motorDesplazamiento;
}

vector<Partida*>& MotorJuego::getPartidas() {
	return this->_partidas;
}

Partida* MotorJuego::correrPartida(Partida* pPartida) {
	_mutex.lock();
	if (pPartida)
		// Declaro la partida en el motor de juego.
		this->_partidas.push_back(pPartida);

	_mutex.unlock();
}

Ticks MotorJuego::notificacionAvanceTiempo(Ticks pTiempo) {
	_mutex.lock();

	// Creo Pajaros
	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end(); ++it) {
		((MotorLanzadorPajaritos*)_MotorLanzadorPajaritos)->correrPartida(
				(*it), pTiempo);
	}

	// Desplazo
	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end(); ++it) {
		((MotorDesplazamiento*)this->_motorDesplazamiento)->correrPartida(
				(*it), pTiempo);
	}

	// Choco y limpio
	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end(); ++it) {
		((MotorColisiones*)this->_MotorColisiones)->correrPartida(
				(*it), pTiempo);
	}

	// Ataco
	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end(); ++it)
		((MotorAtaque*)this->_motorAtaque)->correrPartida((*it), pTiempo);

	_mutex.unlock();
}

Partida* MotorJuego::getPartidaPorJugador(Jugador* pJugador) {
	_mutex.lock();
	// Por defecto el jugador no pertenece a partida alguna.
	Partida* partida = NULL;

	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end() && NULL == partida; ++it) {
		Partida* partidaAux = (*it);
		for (size_t curJugadores = 0; curJugadores < partidaAux->getJugadores(
				).size(); curJugadores++) {
			if (*partidaAux->getJugadores()[curJugadores] == *pJugador) {
				partida = partidaAux;
				break;
			}
		}
	}
	_mutex.unlock();
	return partida;
}

string MotorJuego::getPartidaPorJugadorAString(Jugador* pJugador) {
	_mutex.lock();
	// Por defecto el jugador no pertenece a partida alguna.
	Partida* partida = NULL;

	for (vector<Partida*>::iterator it = this->getPartidas().begin();
			it != this->getPartidas().end() && NULL == partida; ++it) {
		Partida* partidaAux = (*it);
		for (size_t curJugadores = 0; curJugadores < partidaAux->getJugadores(
				).size(); curJugadores++) {
			if (*partidaAux->getJugadores()[curJugadores] == *pJugador) {
				partida = partidaAux;
				break;
			}
		}
	}
	string result;

	if (NULL != partida)
		result = partida->aXML();

	_mutex.unlock();
	return result;
}

Partida* MotorJuego::getPartidaPorNombre(const string& pNombrePartida) {
	_mutex.lock();
	// Por defecto la partida no existe.
	Partida* partida = NULL;

	for (vector<Partida*>::iterator it = this->_partidas.begin();
			it < this->_partidas.end() && partida == NULL; ++it) {
		if ((*it)->getNombre() == pNombrePartida)
			partida = *it;
	}
	_mutex.unlock();
	return partida;
}

vector<string> MotorJuego::listarPartidas() {
	_mutex.lock();
	vector<string> lista;
	if (!this->getPartidas().empty()) {
		for (vector<Partida*>::iterator it = this->_partidas.begin();
				it != this->_partidas.end(); it++)
			lista.push_back((*it)->getNombre());
	}
	_mutex.unlock();
	return lista;
}

void MotorJuego::atacar(Jugador* pJugador, const string& pNombrePartida,
		Posicion* pPosicion) throw (Excepcion) {
	try {
		_mutex.lock();
		((MotorAtaque*) this->_motorAtaque)->atacar(pJugador, pNombrePartida,
				pPosicion);
		_mutex.unlock();
	}
	catch (Excepcion& pEx) {
		_mutex.unlock();
		throw;
	}
}

void MotorJuego::removerJugadorDePartida(Partida* pPartida, Jugador* pJugador) {
	/* Se debe remover el jugador de la partida. Basicamente, se debe remover
	 * del erdito correspondiente.
	 */
	_mutex.lock();

	for (vector<Partida*>::iterator partidasIt = _partidas.begin();
			partidasIt != _partidas.end(); ++partidasIt) {
		if (pPartida->getNombre() == (*partidasIt)->getNombre()) {
			for (vector<Utilidad*>::iterator utilidadesIt = (
					*partidasIt)->ArenaJuego()->getUtilidades().begin();
					utilidadesIt != (*partidasIt)->ArenaJuego()->getUtilidades(
					).end(); ++utilidadesIt) {
				if ((*utilidadesIt)->getRol()->Nombre() == ROL_CERDITO) {
					if ((*((Cerdito*)(*utilidadesIt))->getPropietario()) ==
							*pJugador) {
						// Elimino al jugador como el propietario del cerdito.
						delete ((Cerdito*)(*utilidadesIt))->getPropietario();
						((Cerdito*)(*utilidadesIt))->setPropietario(NULL);
					}
				}
			}

			for (vector<Jugador*>::iterator it = (*partidasIt)->getJugadores(
					).begin(); it != (*partidasIt)->getJugadores().end();
					++it) {
				if (*(*it) == *pJugador) {
					Jugador* aux = (*it);
					(*partidasIt)->getJugadores().erase(it);
					delete aux;
					break;
				}
			}
		}
	}
	_mutex.unlock();
}

void MotorJuego::finalizarPartida(Partida* pPartida) {
	_mutex.lock();
	for (vector<Partida*>::iterator it = _partidas.begin();
			it != _partidas.end(); ++it) {
		if (pPartida->getNombre() == (*it)->getNombre()) {
			_partidas.erase(it);
			break;
		}
	}
	_mutex.unlock();
}

bool MotorJuego::cerditoAvatarDisponibles(const string& pNombrePartida) {
	Partida* partida = this->getPartidaPorNombre(pNombrePartida);
	if (partida)
		return (NULL != partida->ArenaJuego()->getCerditoAvatar());

	return false;
}

void MotorJuego::asignarJugadorACerditoAvatar(const string& pNombrePartida,
		Jugador* pJugador) {
	Partida* partida = this->getPartidaPorNombre(pNombrePartida);
	if (partida) {
		Cerdito* avatar = (Cerdito*)partida->ArenaJuego()->getCerditoAvatar();
		if (avatar) {
			Jugador *aux = new Jugador();
			aux->deElementoXML(pJugador->aElementoXML());
			partida->addJugador(pJugador);
			avatar->setPropietario(aux);
		}
	}
}